package com.xbts.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xbts.common.config.XbtsConfig;
import com.xbts.common.constant.Constants;
import com.xbts.common.enums.EmailTypeEnum;
import com.xbts.common.exception.ServiceException;
import com.xbts.common.utils.*;
import com.xbts.common.utils.file.FileUploadUtils;
import com.xbts.system.domain.CnInvoiceManagement;
import com.xbts.system.domain.CnOrderItem;
import com.xbts.system.domain.SysMessage;
import com.xbts.system.domain.SysUserInvoiceInformation;
import com.xbts.system.domain.dto.EmailSendDTO;
import com.xbts.system.mapper.CnInvoiceManagementMapper;
import com.xbts.system.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 发票管理业务层处理
 *
 * @author xbts
 * @date 2023-08-07
 */
@Service
@Slf4j
public class CnInvoiceManagementServiceImpl extends ServiceImpl<CnInvoiceManagementMapper, CnInvoiceManagement> implements ICnInvoiceManagementService {

    @Autowired
    private CnInvoiceManagementMapper cnInvoiceManagementMapper;

    @Autowired
    private ICnOrderItemService orderItemService;
    @Autowired
    private ISysUserInvoiceInformationService sysUserInvoiceInformationService;
    @Autowired
    private ISysMessageService sysMessageService;
    @Autowired
    private EmailSendService emailSendService;
    @Autowired
    private ISysUserService userService;

    private final static String EMAIL_SUBJECT = "中酒云电子发票";

    /**
     * 查询发票
     *
     * @return invoiceManagement表
     */
    @Override
    public List<CnInvoiceManagement> selectCnInvoiceManagement(CnInvoiceManagement cnInvoiceManagement) {
        QueryWrapper<CnInvoiceManagement> queryWrapper = QueryWrapperUtil.getQueryWrapper(cnInvoiceManagement);
        queryWrapper.eq(StringUtils.isNotEmpty(cnInvoiceManagement.getFeesType()), "cim.fees_type", cnInvoiceManagement.getFeesType());
        queryWrapper.like(StringUtils.isNotEmpty(cnInvoiceManagement.getNickName()), "su.nick_name", cnInvoiceManagement.getNickName());
        queryWrapper.eq(StringUtils.isNotEmpty(cnInvoiceManagement.getInvoiceStatus()), "cim.invoice_status", cnInvoiceManagement.getInvoiceStatus());
        queryWrapper.like(StringUtils.isNotEmpty(cnInvoiceManagement.getInvoiceHandlingPersonnel()), "cim.invoice_handling_personnel", cnInvoiceManagement.getInvoiceHandlingPersonnel());

        if(!SecurityUtils.isManager()){
            queryWrapper.eq("cim.user_id", SecurityUtils.getUserId());
        }
        Date invoiceDate = cnInvoiceManagement.getInvoiceDate();
        if(StringUtils.isNotNull(invoiceDate)){
            String dateStr = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, invoiceDate);
            String endDateStr = dateStr.substring(0, 10) + " 23:59:59";
            queryWrapper.ge("cim.invoice_date", dateStr);
            queryWrapper.le("cim.invoice_date", endDateStr);
        }
        queryWrapper.orderByDesc("cim.create_time");
        List<CnInvoiceManagement> list = cnInvoiceManagementMapper.getList(queryWrapper);

        for (CnInvoiceManagement invoiceManagement : list) {
            SysUserInvoiceInformation invoiceInformation = sysUserInvoiceInformationService.getById(invoiceManagement.getInvoiceInformationId());
            invoiceManagement.setSysUserInvoiceInformation(invoiceInformation);
        }
        return list;
    }

    /**
     * 新增发票
     *
     * @param cnInvoiceManagement 发票
     * @return 结果
     */
    @Override
    @Transactional
    public boolean insertCnInvoiceManagement(CnInvoiceManagement cnInvoiceManagement) {

        // 校验订单是否可以开票
        List<CnOrderItem> cnOrderItems = checkOrderItem(cnInvoiceManagement);

        cnInvoiceManagement.setCreateBy(SecurityUtils.getUserId().toString());
        // 默认已开票
        cnInvoiceManagement.setInvoiceStatus("2");
        cnInvoiceManagement.setInvoiceDate(new Date());
        cnInvoiceManagement.setInvoiceHandlingPersonnel(SecurityUtils.getUserId().toString());

        cnInvoiceManagement.setOrderItemSn(cnOrderItems.stream().map(CnOrderItem::getSn).collect(Collectors.joining(",")));
        cnInvoiceManagement.setShoppingCarEnums(cnInvoiceManagement.getType());

        // 更新子订单发票状态
        updateOrder(cnInvoiceManagement, "2");

        SysUserInvoiceInformation sysUserInvoiceInformation = cnInvoiceManagement.getSysUserInvoiceInformation();
        String id = SnowFlakeUtil.getSnowFlakeId().toString();
        if(StringUtils.isNull(sysUserInvoiceInformation)){
            List<SysUserInvoiceInformation> list = sysUserInvoiceInformationService.list(new LambdaQueryWrapper<SysUserInvoiceInformation>()
                    .eq(SysUserInvoiceInformation::getUserId, cnInvoiceManagement.getUserId())
                    .orderByAsc(SysUserInvoiceInformation::getCreateTime));
            if(list.size() <= 0 && !SecurityUtils.isManager()){
                throw new ServiceException("请填写发票详细信息！");
            } else {
                sysUserInvoiceInformation = list.get(0);
                sysUserInvoiceInformation.setUserId(SecurityUtils.getUserId());
                sysUserInvoiceInformation.setCreateTime(new Date());
                sysUserInvoiceInformation.setId(id);
                sysUserInvoiceInformationService.save(sysUserInvoiceInformation);
                cnInvoiceManagement.setInvoiceInformationId(id);
            }
        }
        return save(cnInvoiceManagement);
    }

    private List<CnOrderItem> checkOrderItem(CnInvoiceManagement cnInvoiceManagement) {
        List<CnOrderItem> list = orderItemService.list(new LambdaQueryWrapper<CnOrderItem>()
                .in(CnOrderItem::getOrderItemId, Arrays.asList(cnInvoiceManagement.getOrderItemId().split(","))));
        for (CnOrderItem orderItem : list) {
            if(!StringUtils.equals("0", orderItem.getInvoicing())){
                throw new RuntimeException("存在已开票或待开票的订单！");
            }
        }
        return list;
    }

    /**
     * 删除单个发票
     *
     * @param invoiceManagementId 主键id
     * @return 结果
     */
    @Override
    public boolean deleteContentById(Long invoiceManagementId) {

        return removeById(invoiceManagementId);
    }

    /**
     * 批量删除发票记录
     *
     * @param ids 多个主键id
     * @return 结果
     */
    @Override
    public boolean batchDeleteContentById(List<Long> ids) {
        return removeBatchByIds(ids);
    }

    /**
     * 更新发票
     *
     * @param cnInvoiceManagement 发票
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateInvoiceManagement(CnInvoiceManagement cnInvoiceManagement, MultipartFile file) {
        CnInvoiceManagement db = getById(cnInvoiceManagement.getInvoiceManagementId());
        if (Objects.isNull(db)) {
            throw new RuntimeException("发票不存在");
        }

        // 上传文件
        if (file != null) {
            // 上传文件路径
            String filePath = XbtsConfig.getUploadPath();
            // 上传并返回新文件名称
            try {
                String fileName = FileUploadUtils.upload(filePath, file);
                cnInvoiceManagement.setFileUrl(fileName);
            } catch (IOException e) {
                log.error("上传文件失败", e);
            }
        }
        SysUserInvoiceInformation sysUserInvoiceInformation = sysUserInvoiceInformationService.getById(cnInvoiceManagement.getInvoiceInformationId());

        if(StringUtils.equals("1", db.getInvoiceStatus()) && StringUtils.isNotEmpty(cnInvoiceManagement.getFileUrl())) {
            cnInvoiceManagement.setInvoiceStatus("2");
            cnInvoiceManagement.setInvoiceHandlingPersonnel(SecurityUtils.getUserId().toString());

            // 更新子订单发票状态
            updateOrder(db, "2");
        }
        boolean flag = updateById(cnInvoiceManagement);
        if (flag && null != sysUserInvoiceInformation && sysUserInvoiceInformation.isWhetherElectronicInvoice()) {
            String fileUrl = cnInvoiceManagement.getFileUrl();
            if (StringUtils.isEmpty(fileUrl)) {
                throw new RuntimeException("请上传发票文件！");
            }
            emailSendService.sendFile(sysUserInvoiceInformation.getEmail(), EMAIL_SUBJECT, fileUrl);
        }

        return flag;
    }

    private void updateOrder(CnInvoiceManagement db, String val) {
        LambdaUpdateWrapper<CnOrderItem> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.in(CnOrderItem::getOrderItemId, Arrays.asList(db.getOrderItemId().split(",")));
        updateWrapper.set(CnOrderItem::getInvoicing, val);
        orderItemService.update(updateWrapper);
    }

    /**
     * 申请开票
     *
     *
     * @param cnInvoiceManagement@return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean applyForInvoicing(CnInvoiceManagement cnInvoiceManagement) {
        if (StringUtils.isEmpty(cnInvoiceManagement.getOrderItemId())){
            throw new RuntimeException("订单id不能为空");
        }
        // 校验订单是否可以开票
        List<CnOrderItem> list = checkOrderItem(cnInvoiceManagement);

        cnInvoiceManagement.setCreateBy(SecurityUtils.getUserId().toString());
        cnInvoiceManagement.setUserId(SecurityUtils.getUserId());
        // 默认开票中
        cnInvoiceManagement.setInvoiceStatus("1");
        cnInvoiceManagement.setFeesType(cnInvoiceManagement.getSysUserInvoiceInformation().getInvoiceType().toString());
        cnInvoiceManagement.setInvoiceDate(new Date());

        cnInvoiceManagement.setShoppingCarEnums(list.get(0).getShoppingCarEnums());
        cnInvoiceManagement.setOrderItemSn(list.stream().map(CnOrderItem::getSn).collect(Collectors.joining(",")));
        // 更新子订单发票状态
        updateOrder(cnInvoiceManagement, "1");


        BigDecimal reduce = list.stream().map(CnOrderItem::getUpdatePrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        cnInvoiceManagement.setAmount(reduce.doubleValue());

        SysUserInvoiceInformation sysUserInvoiceInformation = cnInvoiceManagement.getSysUserInvoiceInformation();
        sysUserInvoiceInformation.setUserId(SecurityUtils.getUserId());
        sysUserInvoiceInformation.setCreateTime(new Date());
        sysUserInvoiceInformationService.save(sysUserInvoiceInformation);
        cnInvoiceManagement.setInvoiceInformationId(sysUserInvoiceInformation.getId());

        return save(cnInvoiceManagement);
    }

    /**
     * 处理发票
     *
     * @param url  图片地址
     * @param file
     * @return 结果
     */
    @Override
    public boolean updateFileUrl(String url, Long invoiceId, MultipartFile file) {

        UpdateWrapper<CnInvoiceManagement> updateWrapper = new UpdateWrapper<>();
        CnInvoiceManagement cnInvoiceManagement;
        QueryWrapper<CnInvoiceManagement> queryWrapper = new QueryWrapper<>();
        cnInvoiceManagement = getOne(queryWrapper.eq("invoice_management_id", invoiceId));
        Long userId = cnInvoiceManagement.getUserId();
        SysUserInvoiceInformation sysUserInvoiceInformation = cnInvoiceManagementMapper.selectInformation(userId);
        String email = sysUserInvoiceInformation.getEmail();
//        if (sysUserInvoiceInformation.isWhetherElectronicInvoice()) {
//            sendEmail(email, file);
//        }
        return update(updateWrapper.eq("invoice_management_id", invoiceId)
                .set("invoice_status", 2)
                .set("file_url", url));
    }

    @Override
    public boolean batchDelete(List<Long> ids) {
        for (Long id : ids) {
            CnInvoiceManagement invoiceManagement = getById(id);

            // 发送消息
            String title = "发票作废通知";
            String content = "编号为：【" + id + "】的发票已于" + DateUtils.getTime() + "作废。";
            SysMessage message = new SysMessage();
            message.setUserIds(Collections.singletonList(invoiceManagement.getUserId()));
            message.setNoticeType(2);
            message.setNoticeTitle(title);
            message.setNoticeContent(content);
            message.setIsAll(1);
            sysMessageService.addSysMessage(message);

            // 发送邮件
            //获取用户邮箱地址
            String email = userService.getEmailByUserId(invoiceManagement.getUserId());
            emailSendService.sendMessage(new EmailSendDTO(email, title, EmailTypeEnum.INVOICE_INVALIDATION, String.valueOf(id),DateUtils.getTime()));

            String[] orderIds = invoiceManagement.getOrderItemId().split(",");
            List<String> idList = Arrays.asList(orderIds);
            // 更新订单开票的状态
            LambdaUpdateWrapper<CnOrderItem> orderUpdateWrapper = new LambdaUpdateWrapper<>();
            orderUpdateWrapper.in(CnOrderItem::getOrderItemId, idList);
            orderUpdateWrapper.set(CnOrderItem::getInvoicing, "0");
            orderItemService.update(orderUpdateWrapper);
        }

        return this.removeBatchByIds(ids);
    }




    @Override
    public void download(HttpServletResponse response, CnInvoiceManagement invoiceManagement) {
        CnInvoiceManagement cnInvoiceManagement = getById(invoiceManagement.getInvoiceManagementId());
        try {
            List<String> fileTypes = new ArrayList<>();
            List<ByteArrayOutputStream> outputStreams = new ArrayList<>();

            // 管理员创建的发票是文件地址，需要先将文件下载到本地
            String[] split = cnInvoiceManagement.getFileUrl().split(",");
            for (String s : split) {
                if (StringUtils.isNotEmpty(s)) {
                    // 本地资源路径
                    String localPath = XbtsConfig.getProfile();
                    // 数据库资源地址
                    String downloadPath = localPath + StringUtils.substringAfter(s, Constants.RESOURCE_PREFIX);
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    try {
                        FileInputStream fileInputStream = new FileInputStream(downloadPath);
                        if (fileInputStream.available() == 0) {
                            throw new ServiceException("文件不存在");
                        }
                        IOUtils.copy(fileInputStream, byteArrayOutputStream);
                        outputStreams.add(byteArrayOutputStream);
                    } catch (IOException e) {
                        log.error("下载合同附件失败", e);
                    }
                    fileTypes.add(StringUtils.substringAfterLast(s, "."));
                    continue;
                }  // 根据订单和合同生成word文档
            }

            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");

            // 设置响应头
            if (split.length > 0) {
                if (outputStreams.size() == 1) {
                    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("发票附件." + StringUtils.substringAfterLast(split[0], "."), "UTF-8"));
                } else {
                    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("发票附件.zip", "UTF-8"));
                }
            }
            createFile(outputStreams, response.getOutputStream(),fileTypes);
        } catch (IOException e) {
            log.error("下载发票附件失败", e);
        }
    }

    @Override
    public void testSendEmail(String email, String fileUrl) {
        // 发送邮件
//        emailSendService.sendMessage(email, EMAIL_SUBJECT, fileUrl);
        emailSendService.sendFile(email, EMAIL_SUBJECT, fileUrl);
    }

    /**
     * 根据订单和合同生成word文档,并返回文件流, 一个文件返回文件本省，多个文件返回压缩包
     * @param outputStreams        文件流
     * @param responseOutputStream 响应流
     * @param fileTypes           文件类型
     */
    private void createFile(List<ByteArrayOutputStream> outputStreams, ServletOutputStream responseOutputStream, List<String> fileTypes) {
        try {
            if (outputStreams.size() == 1) {
                ByteArrayOutputStream byteArrayOutputStream = outputStreams.get(0);
                byte[] bytes = byteArrayOutputStream.toByteArray();
                IOUtils.copy(new ByteArrayInputStream(bytes), responseOutputStream);
            } else {
                // 压缩文件
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
                for (int i = 0; i < outputStreams.size(); i++) {
                    ByteArrayOutputStream outputStream = outputStreams.get(i);
                    ZipEntry zipEntry = new ZipEntry("发票" + (i + 1) + "." + fileTypes.get(i));
                    try {
                        zipOutputStream.putNextEntry(zipEntry);
                        zipOutputStream.write(outputStream.toByteArray());
                        zipOutputStream.closeEntry();
                    } catch (IOException e) {
                        log.error("压缩文件失败", e);
                    }
                }
                try {
                    zipOutputStream.close();
                } catch (IOException e) {
                    log.error("压缩文件失败", e);
                }
                byte[] bytes = byteArrayOutputStream.toByteArray();
                IOUtils.copy(new ByteArrayInputStream(bytes), responseOutputStream);
            }
        } catch (IOException e) {
            log.error("下载发票附件失败", e);
        }
    }
}